package cn.itcast.backend3;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.security.InvalidParameterException;
import java.util.Arrays;
import java.util.IdentityHashMap;
import java.util.Map;

public class ObjectUtil {

    public static void copyProperties(Object source, Object target, String[] properties) {
        if (properties == null || properties.length == 0) {
            throw new InvalidParameterException("Properties array cannot be empty or null.");
        }

        Map<Object, Object> alreadyCopied = new IdentityHashMap<>();

        for (String property : properties) {
            try {
                Field sourceField = findField(source.getClass(), property);
                Field targetField = findField(target.getClass(), property);

                if (sourceField.getType() != targetField.getType()) {
                    throw new RuntimeException("Field types do not match for property: " + property);
                }

                sourceField.setAccessible(true);
                targetField.setAccessible(true);

                Object value = sourceField.get(source);
                if (value != null && needsDeepCopy(value)) {
                    value = deepCopy(value, alreadyCopied);
                }
                targetField.set(target, value);

            } catch (IllegalAccessException e) {
                throw new RuntimeException("Cannot access property: " + property, e);
            }
        }
    }

    private static Field findField(Class<?> clazz, String name) {
        Class<?> searchType = clazz;
        while (Object.class != searchType && searchType != null) {
            Field[] fields = searchType.getDeclaredFields();
            for (Field field : fields) {
                if (name.equals(field.getName())) {
                    return field;
                }
            }
            searchType = searchType.getSuperclass();
        }
        throw new RuntimeException("Could not find field '" + name + "' on class " + clazz);
    }

    private static boolean needsDeepCopy(Object value) {
        return !(value instanceof String || value instanceof Number || value instanceof Boolean || value instanceof Character);
    }

    private static Object deepCopy(Object source, Map<Object, Object> alreadyCopied) {
        if (alreadyCopied.containsKey(source)) {
            return alreadyCopied.get(source);
        }

        if (source == null || source instanceof String || source instanceof Number || source instanceof Boolean || source instanceof Character) {
            return source;
        } else if (source instanceof Object[]) {
            Object[] array = (Object[]) source;
            Object[] copy = Arrays.copyOf(array, array.length, array.getClass());
            alreadyCopied.put(source, copy);
            for (int i = 0; i < array.length; i++) {
                copy[i] = deepCopy(array[i], alreadyCopied);
            }
            return copy;
        } else {
            try {
                Object copy = source.getClass().newInstance();
                alreadyCopied.put(source, copy);
                for (Field field : source.getClass().getDeclaredFields()) {
                    if (!Modifier.isStatic(field.getModifiers())) {
                        field.setAccessible(true);
                        field.set(copy, deepCopy(field.get(source), alreadyCopied));
                    }
                }
                return copy;
            } catch (InstantiationException | IllegalAccessException e) {
                throw new RuntimeException("Failed to deep copy object: " + source, e);
            }
        }
    }

    // Test cases and other methods would go here
}
